﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Q.Extensions
{
    public static class NumberToBytesUnitExtensions
    {
        private static readonly string[] _units = new string[] 
        { "Bytes", "KB", "MB", "GB", "TB", "PB",
            "EB", "ZB", "YB", "NB", "DB", "CB" };

        private static string ToBytesUnit<TNumber>(
            TNumber number)
        {
            return ToBytesUnit(number, "B,-1");
        }

        private static string ToBytesUnit<TNumber>(
            TNumber number, string format)
        {
            var options = format.Split(",".ToCharArray(), 
                StringSplitOptions.RemoveEmptyEntries);

            string type = String.Empty;
            int digits = -1;

            if (options.Length > 2)
            {
                return String.Empty;
            }
            else
            {
                type = options[0];

                if (options.Length == 2)
                {
                    if (!Int32.TryParse(options[1], out digits))
                    {
                        return String.Empty;
                    }
                }

                switch (type)
                {
                    case "B":
                        {
                            try
                            {
                                CultureInfo ci = new CultureInfo("en-us");
                                Double value = (Double)Convert.ChangeType(
                                    number, typeof(Double), ci);

                                int n = 0;
                                while (Math.Abs(value) >= 1024.0 &&
                                    n < _units.Length - 1)
                                {
                                    value /= 1024.0;
                                    n++;
                                }

                                if (digits >= 0)
                                {
                                    value = Math.Round(value, digits);
                                }

                                return value.ToString() + _units[n];
                            }
                            catch (Exception)
                            {
                                return String.Empty;
                            }
                        }
                    default:
                        return String.Empty;
                }
            }
        }

        public static string ToBytesUnitString(this Single number,
            string format)
        {
            return ToBytesUnit<Single>(number, format);
        }

        public static string ToBytesUnitString(this Double number,
            string format)
        {
            return ToBytesUnit<Double>(number, format);
        }

        public static string ToBytesUnitString(this Decimal number,
            string format)
        {
            return ToBytesUnit<Decimal>(number, format);
        }

        public static string ToBytesUnitString(this Int16 number,
            string format)
        {
            return ToBytesUnit<Int16>(number, format);
        }

        public static string ToBytesUnitString(this Int32 number,
            string format)
        {
            return ToBytesUnit<Int32>(number, format);
        }

        public static string ToBytesUnitString(this Int64 number,
            string format)
        {
            return ToBytesUnit<Int64>(number, format);
        }

        public static string ToBytesUnitString(this UInt16 number,
            string format)
        {
            return ToBytesUnit<UInt16>(number, format);
        }

        public static string ToBytesUnitString(this UInt32 number,
            string format)
        {
            return ToBytesUnit<UInt32>(number, format);
        }

        public static string ToBytesUnitString(this UInt64 number,
            string format)
        {
            return ToBytesUnit<UInt64>(number, format);
        }

        public static string ToBytesUnitString(this Single number)
        {
            return ToBytesUnit<Single>(number);
        }

        public static string ToBytesUnitString(this Double number)
        {
            return ToBytesUnit<Double>(number);
        }

        public static string ToBytesUnitString(this Decimal number)
        {
            return ToBytesUnit<Decimal>(number);
        }

        public static string ToBytesUnitString(this Int16 number)
        {
            return ToBytesUnit<Int16>(number);
        }

        public static string ToBytesUnitString(this Int32 number)
        {
            return ToBytesUnit<Int32>(number);
        }

        public static string ToBytesUnitString(this Int64 number)
        {
            return ToBytesUnit<Int64>(number);
        }

        public static string ToBytesUnitString(this UInt16 number)
        {
            return ToBytesUnit<UInt16>(number);
        }

        public static string ToBytesUnitString(this UInt32 number)
        {
            return ToBytesUnit<UInt32>(number);
        }

        public static string ToBytesUnitString(this UInt64 number)
        {
            return ToBytesUnit<UInt64>(number);
        }
    }
}
